home *** CD-ROM | disk | FTP | other *** search
/ The PC-SIG Library 10 / The PC-Sig Library - Shareware for the IBM PC and Compatibles (PC-SIG)(Tenth Edition Disks 1-2804)(1991).iso / PC_SIGCD / 02 / 0 / DISK0202.ZIP / COM_PKG.ASM < prev    next >
Assembly Source File  |  1984-01-01  |  28KB  |  839 lines

  1.     INCLUDE    TITLE.MAC
  2.     .TITLE    <COM_PKG -- COMn: Routines for Lattice C>
  3.     .SBTTL    <History and Copyright Notice>
  4.  
  5. ; com_pkg.asm  1 Dec 83  Craig Milo Rogers at USC/ISI
  6. ;    Corrected a few typos.  Added Deficiencies section.
  7. ;    Clear interrupt controller before polling UART.
  8. ; com_pkg.asm  20 Nov 83  Craig Milo Rogers at USC/ISI
  9. ;    Use int_pkg routines to set/restore interrupt vectors.
  10. ; com_pkg.asm  15 Nov 83  Craig Milo Rogers at USC/ISI
  11. ;    Converted to PDP-11-style TITLEs.
  12. ;    Converted control info to a STRUC.
  13. ; com_pkg.asm  10 Nov 83  Craig Milo Rogers at USC/ISI
  14. ;    Bug fixes in initialization code.
  15. ; com_pkg.asm  30 Oct 83  Craig Milo Rogers at USC/ISI
  16. ;    Support COM1: and COM2:.
  17. ; com_pkg.asm  28 Oct 83  Craig Milo Rogers at USC/ISI
  18. ;    Modified to take transmit and receive buffer addresses and
  19. ; lengths as initialization arguments.
  20. ; com_pkg.asm  26 Oct 83  Craig Milo Rogers at USC/ISI
  21. ;    These routines provide an interrupt-driven circular buffer
  22. ; interface to the COM1: device.  This version interfaces with the
  23. ; multi-model Lattice C compiler version 1.05.  Earlier history:
  24. ;
  25. ; COM_PKG1 provides a library of serial port routines
  26. ; Adapted from code by John Romkey and Jerry Saltzer of MIT
  27. ; by Richard Gillmann (GILLMANN@ISIB), 1983
  28. ;
  29.  
  30.     .SBHED    Overview
  31.  
  32. ;    This is a module of routines for interfacing with the
  33. ; COM1: communications interface on the IBM PC.  The code has
  34. ; been carefully constructed to properly drive the 8250 UART
  35. ; and the 8259 Interrupt Controller.  External circular buffers
  36. ; are used for transmit and receive.
  37.  
  38. ;    Entry points (Lattice C 1.05 calling conventions):
  39.  
  40. ; void
  41. ; com_ini(unit, divisor, tbuf, tbuflen, rbuf, rbuflen)
  42. ;            /* Initializes port and interrupt vector. */
  43. ; int unit;        /* 1 ==> COM1:, 2 ==> COM2:. */
  44. ; int divisor;        /* Baud rate generator divisor. */
  45. ; char *tbuf;        /* Transmit buffer address. */
  46. ; int tbuflen;        /* Transmit buffer length. */
  47. ; char *rbuf;        /* Receive buffer address. */
  48. ; int rbuflen;        /* Receive buffer length. */
  49.  
  50. ; void
  51. ; com_trm(unit)        /* Turns off interrupts from the aux port. */
  52. ; int unit;        /* 1 ==> COM1:, 2 ==> COM2:. */
  53.  
  54. ; void
  55. ; com_doff(unit)    /* Turns off DTR. */
  56. ; int unit;        /* 1 ==> COM1:, 2 ==> COM2:. */
  57.  
  58. ; void
  59. ; com_don(unit)        /* Turns on DTR. */
  60. ; int unit;        /* 1 ==> COM1:, 2 ==> COM2:. */
  61.  
  62. ; int            /* Number of characters in input buffer. */
  63. ; com_icnt(unit)    /* Returns number of characters in input buffer. */
  64. ; int unit;        /* 1 ==> COM1:, 2 ==> COM2:. */
  65.  
  66. ; int            /* Next character in input buffer or EOF. */
  67. ; com_getc(unit)    /* Reads next character in input buffer. */
  68. ; int unit;        /* 1 ==> COM1:, 2 ==> COM2:. */
  69.  
  70. ; int            /* Number of free bytes in output buffer. */
  71. ; com_ocnt(unit)    /* Returns number of free bytes in output buffer. */
  72. ; int unit;        /* 1 ==> COM1:, 2 ==> COM2:. */
  73.  
  74. ; bool            /* Returns FALSE if no more room. */
  75. ; com_putc(unit, ch)    /* Writes a character to the output buffer. */
  76. ; int unit;        /* 1 ==> COM1:, 2 ==> COM2:. */
  77. ; char ch;        /* The character to write. */
  78.  
  79. ; bool            /* Returns FALSE if no more room. */
  80. ; com_loopc(unit, ch)    /* Writes a character to the input buffer. */
  81. ; int unit;        /* 1 ==> COM1:, 2 ==> COM2:. */
  82. ; char ch;        /* The character to write. */
  83.  
  84. ; void
  85. ; com_bon(unit)        /* Turns on BREAK. */
  86. ; int unit;        /* 1 ==> COM1:, 2 ==> COM2:. */
  87.  
  88. ; void
  89. ; com_boff(unit)    /* Turns off BREAK. */
  90. ; int unit;        /* 1 ==> COM1:, 2 ==> COM2:. */
  91.  
  92. ; void
  93. ; com_break(unit)    /* Sends complete BREAK sequence. */
  94. ; int unit;        /* 1 ==> COM1:, 2 ==> COM2:. */
  95.  
  96.     .SBHED    Deficiencies
  97.  
  98. ; 1)    The initialization routine should pre-calculate the 8250
  99. ;    addresses, and store them in the control block.  This will
  100. ;    save a few percent of code space, and speed up the interrupt
  101. ;    service routine.
  102.  
  103. ; 2)    There should be a puts() routine to optimize the common case
  104. ;    of transmitting a buffer of characters.  A gets() routine
  105. ;    would be desirable for symmetry, although the time savings is
  106. ;    less likely to be significant.
  107.  
  108. ; 3)    There should be finer control over the UART initialization.
  109. ;    It might also be nice to be able to change UART parameters
  110. ;    dynamically.
  111.  
  112. ; 4)    There should be a way to respond to modem control signals,
  113. ;    such as Ring Indicator, and to generate modem control signals
  114. ;    besides DTR.
  115.  
  116. ; 5)    The com_break() routine has it's name truncated.  This should be
  117. ;    repaired when C supports long names.  The int_setup()
  118. ;    external, etc. are also affected.
  119.  
  120. ; 6)    There should be provision for addional COM units.
  121.  
  122. ; 7)    The COM register base addresses should be obtained from the BIOS.
  123.  
  124. ; 8)    It should be possible for COM units to share an interrupt level.
  125.  
  126. ; 9)    The error returns from int_setup() and int_restore() should be
  127. ;    checked.
  128.  
  129. ; 10)    Perhaps the initialization code can be rewritten so interrupts
  130. ;    don't have to be disabled for quite so long.
  131.  
  132.     .SBHED    Declarations
  133.  
  134. IF1
  135.     INCLUDE    DOS.MAC    ; C segments.
  136.     INCLUDE BMAC.MAC    ; C calling conventions.
  137. ENDIF
  138.  
  139.                 ; int_pkg routines:
  140.     BEXTRN    INT_SETU    ; Setup an interrupt vector.
  141.     BEXTRN    INT_REST    ; Restore an interrupt vector.
  142.  
  143.                 ; COM1: parameters:
  144. COM1_INT     EQU     4        ; Interrupt number for comm. port.
  145. COM1_BASE    EQU     3F8H    ; Base address of 8250 registers.
  146.  
  147.                 ; COM2: parameters:
  148. COM2_INT     EQU     3        ; Interrupt number for comm. port.
  149. COM2_BASE    EQU     2F8H    ; Base address of 8250 registers.
  150.  
  151.  
  152. INT_OFF EQU     08H        ; Converts 8259 interrupt numbers to
  153.                 ; 8088 interrupt numbers.
  154.  
  155.                 ; 8250 device registers:
  156. DATREG  EQU     0H        ; Data register.
  157. DLL     EQU     0H        ; Low divisor latch.
  158. DLH     EQU     1H        ; High divisor latch.
  159. IER     EQU     1H        ; Interrupt enable register.
  160. IIR     EQU     2H        ; Interrupt identification register.
  161. LCR     EQU     3H        ; Line control register.
  162. MCR     EQU     4H        ; Modem control register.
  163. LSR     EQU     5H        ; Line status register.
  164. MSR     EQU     6H        ; Modem status register.
  165.  
  166. DLA     EQU     80H             ; Divisor latch access.
  167. MODE    EQU     03H             ; 8-bits, no parity.
  168. DTR     EQU     0BH             ; Bits to set dtr line.
  169. DTR_OF  EQU     00H             ; Turn off dtr, rts, and the interupt driver.
  170. THRE    EQU     20H             ; Mask to find status of xmit holding reg.
  171. RXINT   EQU     01H             ; Enable data available interrupt.
  172. TXINT   EQU     02H             ; Enable tx holding register empty interrupt.
  173. TCHECK  EQU     20H             ; Mask for checking tx reg stat on interrupt.
  174. RCHECK  EQU     01H             ; Mask for checking rx reg stat on interrupt.
  175. INT_PEND EQU    01H             ; There is an interrupt pending.
  176. MSTAT   EQU     00H             ; Modem status interrupt.
  177. WR      EQU     02H             ; Ready to xmit data.
  178. RD      EQU     04H             ; Received data interrupt.
  179. LSTAT   EQU     06H             ; Line status interrupt.
  180. ACK     EQU     244             ; Acknowledge symbol.
  181. PARITY  EQU     7FH             ; Bits to mask off parity.
  182. BREAK   EQU     40H             ; Bits to cause break.
  183.  
  184.                 ; 8259 Interrupt Controller:
  185. IMR     EQU     21H             ; Interrupt mask register.
  186. OCW2    EQU     20H             ; Operational control word on 8259.
  187. EOI     EQU     60H        ; Specific end of interrupt.
  188.  
  189.                 ; C return values:
  190. TRUE    EQU     1               ; Truth.
  191. FALSE   EQU     0               ; Falsehood.
  192.  
  193.  
  194. COMX_CTRL    STRUC            ; Control parameters for COMn:
  195.  
  196. TBUF_SEG    DW    ?    ; Transmit buffer segment number.
  197. TBUF_OFF    DW    ?    ; Transmit buffer offset.
  198. TBUF_SIZE    DW    ?    ; Transmit buffer size.
  199.  
  200. START_TDATA     DW      ?       ; Index to first character in x-mit buffer.
  201. END_TDATA       DW      ?       ; Index to first free space in x-mit buffer.
  202. SIZE_TDATA      DW      ?       ; Number of characters in x-mit buffer.
  203.  
  204. RBUF_SEG    DW    ?    ; Receive buffer segment number.
  205. RBUF_OFF    DW    ?    ; Receive buffer offset.
  206. RBUF_SIZE    DW    ?    ; Receive buffer size.
  207.  
  208. START_RDATA     DW      ?       ; Index to first character in rec. buffer.
  209. END_RDATA       DW      ?       ; Index to first free space in rec. buffer.
  210. SIZE_RDATA      DW      ?       ; Number of characters in rec. buffer.
  211.  
  212. COMX_INT    DW    ?    ; Interrupt number for comm. port.
  213. COMX_BASE    DW    ?    ; I/O base address of 8250 registers.
  214.  
  215. COMX_CTRL    ENDS        ; End of the structure definition.
  216.  
  217.     .SBHED    <Data Storage>
  218.  
  219.     DSEG
  220. COM1_CTRL    COMX_CTRL <>    ; Control parameters for COM1:.
  221. COM2_CTRL    COMX_CTRL <>    ; Control parameters for COM2:.
  222.     ENDDS
  223.  
  224.     PSEG            ; All the rest is code.
  225.  
  226.     .SBHED    <COM1: and COM2: Specific Interrupt Handlers>
  227. ;
  228. ; DATASEG - DS for Use by Interrupt Handler
  229. ;
  230. ;    WARNING!
  231. ;    Note the impure use of DATASEG below.  This code is not ROMmable.
  232. ;
  233. DATASEG DW      0        ; Holds our data segment number.
  234.  
  235. ;
  236. ; INT_HNDLR1 - Handles Interrupts Generated by COM1:
  237. ;
  238. INT_HNDLR1 PROC  FAR        ;;; Enter here on interrupt.
  239.     PUSH    SI        ;;; Save old source index.
  240.     MOV    SI,OFFSET COM1_CTRL    ;;; Get pointer to control block.
  241.  
  242.     JMP SHORT INT_COMMON    ;;; Go join common interrupt handler.
  243.  
  244.  
  245. ;
  246. ; INT_HNDLR2 - Handles Interrupts Generated by COM2:
  247. ;
  248. INT_HNDLR2 PROC  FAR        ;;; Enter here on interrupt.
  249.     PUSH    SI        ;;; Save old source index.
  250.     MOV    SI,OFFSET COM2_CTRL    ;;; Get pointer to control block.
  251.                 ;;; Fall into common interrupt handler:
  252.  
  253.     .SBHED    <Common Interrupt Handler>
  254.  
  255. INT_COMMON:
  256.         PUSH    DS        ;;; Save data segment register.
  257.         PUSH    CS:DATASEG    ;;; Set up new data segment.
  258.         POP    DS        ;;;
  259.         PUSH    ES        ;;; Save previous context on existing stack.
  260.         PUSH    BP        ;;;
  261.         PUSH    DI        ;;;
  262.         PUSH    AX        ;;;
  263.         PUSH    BX        ;;;
  264.         PUSH    CX        ;;;
  265.         PUSH    DX        ;;;
  266.  
  267. ;;; Clear the interrupt controller flag before polling interrupt sources
  268. ;;; on the UART to avoid losing additional COM interrupts.
  269.  
  270.         MOV     DX,OCW2         ;;; Tell the 8259 that I'm done.
  271.         MOV     AL,EOI        ;;; Get the End-of-Interrupt code.
  272.     OR    AL,BYTE PTR [SI].COMX_INT ;;; Set to specific int. number.
  273.         OUT     DX,AL        ;;;
  274.  
  275. ;;; Find out where interrupt came from and jump to routine to handle it:
  276.     MOV    DX,[SI].COMX_BASE    ;;;
  277.         ADD    DX,IIR        ;;;
  278.         IN      AL,DX        ;;;
  279.         CMP     AL,RD        ;;;
  280.      JZ    RX_INT          ;;; If it's from the receiver.
  281.         CMP     AL,WR        ;;;
  282.          JZ    TX_INT          ;;; If it's from the transmitter.
  283.         CMP     AL,LSTAT    ;;;
  284.          JZ    LSTAT_INT       ;;; Interrupt becuase of line status.
  285.         CMP     AL,MSTAT    ;;;
  286.          JZ    MSTAT_INT       ;;; Interrupt because of modem status.
  287.         JMP     FAR PTR INT_END ;;; Interrupt when no int. pending, go away.
  288.  
  289. LSTAT_INT:
  290.     MOV    DX,[SI].COMX_BASE    ;;;
  291.         ADD    DX,LSR              ;;; Clear interrupt.
  292.         IN      AL,DX            ;;;
  293.         JMP     REPOLL              ;;; See if any more interrupts.
  294.  
  295. MSTAT_INT:
  296.     MOV    DX,[SI].COMX_BASE    ;;;
  297.         ADD    DX,MSR              ;;; Clear interrupt.
  298.         IN      AL,DX            ;;;
  299.         JMP     REPOLL              ;;; See if any more interrupts.
  300.  
  301. TX_INT:
  302.     MOV    DX,[SI].COMX_BASE    ;;; 
  303.         ADD    DX,LSR            ;;;
  304.         IN      AL,DX            ;;;
  305.         AND     AL,TCHECK        ;;;
  306.      JNZ    GOODTX              ;;; Good interrupt.
  307.         JMP     REPOLL              ;;; See if any more interrupts.
  308.  
  309. GOODTX: CMP     [SI].SIZE_TDATA,0    ;;; See if any more data to send.
  310.      JNE    HAVE_DATA           ;;; If not equal then data to send.
  311.  
  312. ;;; If no data to send then reset tx interrupt and return.
  313.     MOV    DX,[SI].COMX_BASE    ;;;
  314.         ADD    DX,IER            ;;;
  315.         MOV     AL,RXINT        ;;;
  316.         OUT     DX,AL            ;;;
  317.         JMP     REPOLL            ;;;
  318.  
  319. HAVE_DATA:
  320.     MOV    ES,[SI].TBUF_SEG    ;;; Get transmit buffer segment num.
  321.     MOV    DI,[SI].TBUF_OFF    ;;; Get transmit buffer offset.
  322.         MOV     BX,[SI].START_TDATA    ;;; BX points to next char to be sent.
  323.     MOV    DX,[SI].COMX_BASE    ;;;
  324.         ADD    DX,DATREG           ;;; DX equals port to send data to.
  325.         MOV     AL,ES:[BX+DI]       ;;; Get data from buffer.
  326.         OUT     DX,AL               ;;; Send data.
  327.         INC     BX                  ;;; Increment START_TDATA.
  328.         CMP     BX,[SI].TBUF_SIZE    ;;; See if gone past end.
  329.      JB    NTADJ               ;;; If not then skip.
  330.         XOR     BX,BX            ;;; Reset to beginning.
  331. NTADJ:  MOV     [SI].START_TDATA,BX    ;;; Save START_TDATA.
  332.         DEC     [SI].SIZE_TDATA        ;;; One less character in xmit buffer.
  333.         JMP     REPOLL            ;;;
  334.  
  335. RX_INT:
  336.     MOV    DX,[SI].COMX_BASE    ;;;
  337.     ADD    DX,LSR            ;;; Check and see if read is real.
  338.         IN      AL,DX            ;;;
  339.         AND     AL,RCHECK           ;;; Look at receive data bit.
  340.          JNZ    GOOD_RX             ;;; Real, go get byte.
  341.         JMP     REPOLL              ;;; Go look for other interrupts.
  342.  
  343. GOOD_RX:
  344.     MOV    DX,[SI].COMX_BASE    ;;;
  345.         ADD    DX,DATREG        ;;;
  346.         IN      AL,DX               ;;; Get data.
  347.     MOV    DX,[SI].RBUF_SIZE    ;;; Get size of buffer.
  348.         CMP     [SI].SIZE_RDATA,DX    ;;; See if any room for data.
  349.      JAE    REPOLL          ;;; If no room then look for more interrupts.
  350.     MOV    ES,[SI].RBUF_SEG    ;;; Get receive buffer segment number.
  351.     MOV    DI,[SI].RBUF_OFF    ;;; Get receive buffer offset.
  352.         MOV     BX,[SI].END_RDATA    ;;; BX points to free space.
  353.         MOV     ES:[BX+DI],AL   ;;; Send data to buffer.
  354.         INC     [SI].SIZE_RDATA      ;;; Got one more character.
  355.         INC     BX              ;;; Increment END_RDATA pointer.
  356.         CMP     BX,DX        ;;; See if gone past end.
  357.          JB    NRADJ           ;;; If not then skip,
  358.         XOR     BX,BX        ;;;   else adjust to beginning.
  359. NRADJ:  MOV     [SI].END_RDATA,BX    ;;; Save value.
  360.  
  361. REPOLL:
  362.     MOV    DX,[SI].COMX_BASE    ;;; Read the line status register.
  363.     ADD    DX,LSR          ;;; We always expect receive data, so
  364.         IN      AL,DX           ;;;   check status to see if any is ready.
  365.     MOV    BL,AL        ;;; Save for transmit check, below.
  366.         AND     AL,RCHECK       ;;; Get received data bit.
  367.          JNZ    GOOD_RX         ;;; Yes, go accept the byte.
  368.  
  369.         ADD    DX,(IER-LSR)    ;;; Look at transmit condition
  370.         IN      AL,DX           ;;;   to see if we are enabled to send data.
  371.         AND     AL,TXINT    ;;;
  372.          JZ    INT_END         ;;; Not enabled, so go away.
  373.         AND     BL,TCHECK    ;;; Check saved status for xmit done.
  374.          JZ    INT_END        ;;;
  375.         JMP     GOODTX          ;;; Transmitter is finished, go get more data.
  376.  
  377. INT_END:
  378.         POP     DX        ;;; Restore previous context.
  379.         POP     CX        ;;;
  380.         POP     BX        ;;;
  381.         POP     AX        ;;;
  382.         POP     DI        ;;;
  383.         POP     BP        ;;;
  384.         POP     ES        ;;;
  385.         POP     DS        ;;;
  386.     POP    SI        ;;;
  387.         IRET            ;;; Return from interrupt.
  388.  
  389. INT_HNDLR2 ENDP
  390. INT_HNDLR1 ENDP
  391.  
  392.     .SBHED    <SET_SI -- Select COM Control Block>
  393.  
  394. ;    This internal routine is called to point to the
  395. ; appropriate control block.
  396. ;
  397. ; Calling sequence:
  398. ;    MOV    AX,UNIT
  399. ;    CALL    SET_SI
  400.  
  401. SET_SI    PROC    NEAR
  402.     CMP    AX,1        ; Is this for unit 1?
  403.      JNE    SET_CTRL2    ;   (must be for unit 2)
  404.  
  405.     MOV    SI,OFFSET COM1_CTRL ; Point to COM1: control area.
  406.     RET            ; Return to caller.
  407.  
  408. SET_CTRL2:
  409.     MOV    SI,OFFSET COM2_CTRL ; Point to COM2: control area.
  410.     RET            ; Return to caller.
  411.  
  412. SET_SI    ENDP
  413.  
  414.     .SBHED    <COM_INI -- Initialize Communication Port>
  415.  
  416. ; void
  417. ; com_ini(unit, divisor, tbuf, tbuflen, rbuf, rbuflen)
  418. ;            /* Initializes port and interrupt vector. */
  419. ; int unit;        /* 1 ==> COM1:, 2 ==> COM2:. */
  420. ; int divisor;        /* Baud rate generator divisor. */
  421. ; char *tbuf;        /* Transmit buffer address. */
  422. ; int tbuflen;        /* Transmit buffer length. */
  423. ; char *rbuf;        /* Receive buffer address. */
  424. ; int rbuflen;        /* Receive buffer length. */
  425.  
  426. ;    Initialize the Intel 8250 and set up interrupt vector to int_hndlr.
  427.  
  428. IF LDATA
  429.     BENTRY    COM_INI    <UNIT,DIVISOR,TBOFF,TBSEG,TBLEN,RBOFF,RBSEG,RBLEN>
  430. ELSE
  431.     BENTRY    COM_INI    <UNIT,DIVISOR,TBOFF,TBLEN,RBOFF,RBLEN>
  432. ENDIF
  433.     AUTO    <HANDLR>
  434.  
  435.     MOV    AX,UNIT        ; Get the unit number.
  436.     CALL    SET_SI        ; Point to proper control area.
  437.                 ; (Leaves AX unchanged)
  438.     CMP    AX,2        ; Initializing unit #2?
  439.      JE    INIT2        ;   (yes)
  440.  
  441.                 ; Select constants for unit #1:
  442.     MOV    AX,COM1_INT    ; Interrupt number.
  443.     MOV    BX,COM1_BASE    ; I/O base address.
  444.         MOV    CX,OFFSET INT_HNDLR1    ; Start of interrupt handler.
  445.  
  446.     JMP SHORT INITCOM    ; Go join common code.
  447.  
  448. INIT2:                ; Select constants for unit #2:
  449.     MOV    AX,COM2_INT    ; Interrupt number.
  450.     MOV    BX,COM2_BASE    ; I/O base address.
  451.         MOV    CX,OFFSET INT_HNDLR2    ; Start of interrupt handler.
  452.                 ; Fall into INITCOM:
  453.  
  454. INITCOM:
  455.     MOV    [SI].COMX_INT,AX    ; Save the interrupt number.
  456.     MOV    [SI].COMX_BASE,BX    ; Save the I/O base address.
  457.     MOV    HANDLR,CX    ; Save the interrupt handler starting address.
  458.  
  459.         MOV     AX,DS        ; Copy our data segment number.
  460. IFE LDATA
  461.     MOV    ES,AX        ; Save for buffer addresses.
  462. ENDIF
  463.         MOV     CS:DATASEG,AX    ; Store segment # in code space (gulp!).
  464.  
  465. IF LDATA
  466.     MOV    AX,TBSEG    ; Get the transmit buffer segment number.
  467. ENDIF
  468.     MOV    [SI].TBUF_SEG,AX    ; Save it.
  469.     MOV    BX,TBOFF    ; Copy the transmit buffer offset.
  470.     MOV    [SI].TBUF_OFF,BX    ;
  471.     MOV    BX,TBLEN    ; Copy the transmit buffer length.
  472.     MOV    [SI].TBUF_SIZE,BX    ;
  473.  
  474. IF LDATA
  475.     MOV    AX,RBSEG    ; Get the receive buffer segment number.
  476. ENDIF
  477.     MOV    [SI].RBUF_SEG,AX    ; Save it.
  478.     MOV    AX,RBOFF    ; Copy the receive buffer offset.
  479.     MOV    [SI].RBUF_OFF,AX    ;
  480.     MOV    AX,RBLEN    ; Copy the receive buffer length.
  481.     MOV    [SI].RBUF_SIZE,AX    ;
  482.  
  483.     XOR    AX,AX        ; Clear the accumulator.
  484.     MOV    [SI].START_TDATA,AX    ; Reset start of transmitted data.
  485.     MOV    [SI].END_TDATA,AX    ; Reset end of transmitted data.
  486.     MOV    [SI].SIZE_TDATA,AX    ; Reset number of transmitted chars.
  487.  
  488.     MOV    [SI].START_RDATA,AX    ; Reset start of received data.
  489.     MOV    [SI].END_RDATA,AX    ; Reset end of received data.
  490.     MOV    [SI].SIZE_RDATA,AX    ; Reset number of received chars.
  491.  
  492.         CLI            ; ******* Disable Interrupts *******
  493.  
  494.     MOV    DX,[SI].COMX_BASE    ;;;
  495.         ADD    DX,MCR        ;;; Reset the UART (AX is still zero).
  496.         OUT     DX,AL        ;;;
  497.  
  498.         ADD    DX,(LSR-MCR)    ;;; Reset line status condition.
  499.         IN      AL,DX        ;;;
  500.         ADD    DX,(DATREG-LSR)    ;;; Reset receive data condition.
  501.         IN      AL,DX        ;;;
  502.         ADD    DX,(MSR-DATREG)    ;;; Reset modem deltas and conditions.
  503.         IN      AL,DX        ;;;
  504.  
  505.         ADD     DX,(LCR-MSR)    ;;; Set baud rate with the passed argument.
  506.         MOV     AL,DLA+MODE    ;;;
  507.         OUT     DX,AL        ;;;
  508.         ADD    DX,(DLL-LCR)    ;;;
  509.         MOV     AX,DIVISOR    ;;;
  510.         OUT     DX,AL        ;;; Low byte of passed argument.
  511.         ADD    DX,(DLH-DLL)    ;;;
  512.     MOV    AL,AH        ;;;
  513.         OUT     DX,AL            ;;; High byte of passed argument.
  514.  
  515.         ADD    DX,(LCR-DLH)    ;;; Set 8250 to 8 bits, no parity.
  516.         MOV     AL,MODE        ;;;
  517.         OUT     DX,AL        ;;;
  518.  
  519.     PUSH    SI            ;;; Save pointer to COM block.
  520.     MOV    AX,[SI].COMX_INT    ;;; Get the 8259 interrupt number.
  521.     ADD    AX,INT_OFF        ;;; Convert to 8086 interrupt number.
  522.     BCALL    INT_SETU <AX HANDLR CS>    ;;; Call int_setup(vec, newip, newcs).
  523.     POP    SI            ;;; Restore data block pointer.
  524.  
  525.                 ;;; Enable interrupts on 8259 and 8250:
  526.         IN      AL,IMR          ;;; Get current enable bits on 8259.
  527.     MOV    CL,BYTE PTR [SI].COMX_INT    ;;; Get interrupt number.
  528.     MOV    BL,1        ;;; Convert to
  529.     SHL    BL,CL        ;;;   bit position.
  530.     NOT    BL        ;;; Clear current
  531.         AND     AL,BL        ;;;   interrupt bit.
  532.         OUT     IMR,AL        ;;; Set enable on 8259.
  533.     MOV    DX,[SI].COMX_BASE    ;;;
  534.         ADD    DX,IER          ;;; Enable interrupts on 8250.
  535.         MOV     AL,RXINT    ;;;
  536.         OUT     DX,AL        ;;;
  537.         ADD    DX,(MCR-IER)    ;;; Set dtr and enable int driver.
  538.         MOV     AL,DTR        ;;;
  539.         OUT     DX,AL        ;;;
  540.  
  541.         STI            ;;; ******* Enable Interrupts *******
  542.                 ;;; (Next instruction still disabled)
  543.     BEND    COM_INI
  544.  
  545.     .SBHED    <COM_TRM -- Turn Off Interrupts and Shutdown>
  546.  
  547. ; void
  548. ; com_trm(unit)        /* Turns off interrupts from the COM1: port. */
  549. ; int unit;        /* 1 ==> COM1:, 2 ==> COM2:. */
  550.  
  551.     BENTRY    COM_TRM <UNIT>
  552.  
  553.     MOV    AX,UNIT        ; Point to COM1: or COM2: save area.
  554.     CALL    SET_SI        ;
  555.  
  556.     MOV    DX,[SI].COMX_BASE
  557.     ADD    DX,IER        ; Turn off 8250.
  558.         MOV     AL,0
  559.         OUT     DX,AL
  560.  
  561.         IN      AL,IMR        ; Turn off 8259.
  562.     MOV    CL,BYTE PTR [SI].COMX_INT    ; Get interrupt number.
  563.     MOV    BL,1        ; Convert to
  564.     SHL    BL,CL        ;   bit position.
  565.         OR      AL,BL        ; Disable this interrupt.
  566.         OUT     IMR,AL
  567.  
  568.                 ; Reset interrupt vector:
  569.     MOV    AX,[SI].COMX_INT    ; Get the 8259 interrupt number.
  570.     ADD    AX,INT_OFF        ; Convert to 8086 interrupt number.
  571.     BCALL    INT_REST <AX>        ; Call int_restore(vec).
  572.  
  573.     BEND    COM_TRM
  574.  
  575.     .SBHED    <COM_DOFF -- Turn off DTR>
  576.  
  577. ; void
  578. ; com_doff(unit)    /* Turns off DTR. */
  579. ; int unit;        /* 1 ==> COM1:, 2 ==> COM2:. */
  580.  
  581. ;     Turns off DTR to tell modems that the terminal has gone away
  582. ; and to hang up the phone.
  583.  
  584.     BENTRY    COM_DOFF <UNIT>
  585.  
  586.     MOV    AX,UNIT        ; Point to COM1: or COM2: save area.
  587.     CALL    SET_SI        ;
  588.  
  589.     MOV    DX,[SI].COMX_BASE
  590.         ADD    DX,MCR
  591.         MOV     AL,DTR_OF
  592.         OUT     DX,AL
  593.  
  594.     BEND    COM_DOFF
  595.  
  596.     .SBHED    <COM_DON -- Turn On DTR>
  597.  
  598. ; void
  599. ; com_don(unit)        /* Turns on DTR. */
  600. ; int unit;        /* 1 ==> COM1:, 2 ==> COM2:. */
  601.  
  602.     BENTRY    COM_DON <UNIT>
  603.  
  604.     MOV    AX,UNIT        ; Point to COM1: or COM2: save area.
  605.     CALL    SET_SI        ;
  606.  
  607.     MOV    DX,[SI].COMX_BASE
  608.         ADD    DX,MCR
  609.         MOV     AL,DTR
  610.         OUT     DX,AL
  611.  
  612.     BEND    COM_DON
  613.  
  614.     .SBHED    <COM_ICNT -- Return Number of Input Bytes>
  615.  
  616. ; int            /* Number of characters in input buffer. */
  617. ; com_icnt(unit)    /* Returns number of characters in input buffer. */
  618. ; int unit;        /* 1 ==> COM1:, 2 ==> COM2:. */
  619.  
  620.     BENTRY    COM_ICNT <UNIT>
  621.  
  622.     MOV    AX,UNIT        ; Point to COM1: or COM2: save area.
  623.     CALL    SET_SI        ;
  624.  
  625.         MOV     AX,[SI].SIZE_RDATA   ; Get number of bytes used.
  626.  
  627.     BEND    COM_ICNT
  628.  
  629.     .SBHED    <COM_GETC -- Get the Next Received Character>
  630.  
  631. ; int            /* Next character in input buffer or EOF. */
  632. ; com_getc(unit)    /* Reads next character in input buffer. */
  633. ; int unit;        /* 1 ==> COM1:, 2 ==> COM2:. */
  634. ;
  635. ;    Returns the next character from the receive buffer and
  636. ; removes it from the buffer.
  637.  
  638.     BENTRY    COM_GETC <UNIT>
  639.  
  640.     MOV    AX,UNIT        ; Point to COM1: or COM2: save area.
  641.     CALL    SET_SI        ;
  642.  
  643.     CMP    [SI].SIZE_RDATA,0    ; Is there anything in the buffer?
  644.      JE    L12        ;   (nothing)
  645.  
  646.     MOV    ES,[SI].RBUF_SEG    ; Get receive buffer segment number.
  647.     MOV    DI,[SI].RBUF_OFF    ; Get receive buffer offset.
  648.         MOV     BX,[SI].START_RDATA    ; Fetch next data byte.
  649.         MOV     AL,ES:[BX+DI]   ; Get data from buffer.
  650.         XOR     AH,AH
  651.  
  652.         INC     BX              ; Bump START_RDATA so it points at next char.
  653.         CMP     BX,[SI].RBUF_SIZE    ; See if past end.
  654.          JB    L10             ; If not then skip.
  655.         XOR     BX,BX        ; Adjust to beginning.
  656. L10:    MOV     [SI].START_RDATA,BX  ; Save the new START_RDATA value.
  657.  
  658.         DEC     [SI].SIZE_RDATA      ; One less character.
  659.     JMP SHORT L14        ; Skip to common return code.
  660.  
  661. L12:    MOV    AX,-1        ; Indicate no characters available.
  662.  
  663. L14:    BEND    COM_GETC
  664.  
  665.     .SBHED    <COM_OCNT -- Returns Number of Free Bytes>
  666.  
  667. ; int            /* Number of free bytes in output buffer. */
  668. ; com_ocnt(unit)    /* Returns number of free bytes in output buffer. */
  669. ; int unit;        /* 1 ==> COM1:, 2 ==> COM2:. */
  670.  
  671.     BENTRY    COM_OCNT <UNIT>
  672.  
  673.     MOV    AX,UNIT        ; Point to COM1: or COM2: save area.
  674.     CALL    SET_SI        ;
  675.  
  676.         MOV     AX,[SI].TBUF_SIZE    ; Get the size of the x-mit buffer.
  677.         SUB     AX,[SI].SIZE_TDATA   ; Subtract the number of bytes used.
  678.  
  679.     BEND    COM_OCNT
  680.  
  681.     .SBHED    <COM_PUTC -- Queue a Character for Output>
  682.  
  683. ; bool            /* Returns FALSE if no more room. */
  684. ; com_putc(unit, ch)    /* Writes a character to the output buffer. */
  685. ; int unit;        /* 1 ==> COM1:, 2 ==> COM2:. */
  686. ; char ch;        /* The character to write. */
  687.  
  688. ;    Note that there is an implicit interlock with the interrupt
  689. ; level.  It is OK for an interrupt to occur between incrementing
  690. ; SIZE_TDATA and the end of the code that monkeys with the interrupt
  691. ; enable bits.  The worst that can happen is that there will be an
  692. ; extra interrupt, which will be ignored (because SIZE_TDATA will be
  693. ; zero again by then).
  694.  
  695.     BENTRY    COM_PUTC <UNIT,OCHAR>
  696.  
  697.     MOV    AX,UNIT        ; Point to COM1: or COM2: save area.
  698.     CALL    SET_SI        ;
  699.  
  700.         MOV     AX,[SI].TBUF_SIZE    ; Get the size of the x-mit buffer.
  701.         SUB     AX,[SI].SIZE_TDATA   ; Subtract the number of bytes used.
  702.      JE    L24        ; No more free space.
  703.  
  704.     MOV    ES,[SI].TBUF_SEG    ; Get transmit buffer segment number.
  705.     MOV    DI,[SI].TBUF_OFF    ; Get transmit buffer offset.
  706.         MOV     BX,[SI].END_TDATA    ; BX points to free space.
  707.         MOV     AL,OCHAR        ; Move data from stack to x-mit buffer.
  708.         MOV     ES:[BX+DI],AL
  709.         INC     BX              ; Increment END_TDATA to point to free space.
  710.         CMP     BX,[SI].TBUF_SIZE    ; See if past end.
  711.          JB      L20        ; If not then skip.
  712.         XOR     BX,BX        ; Adjust to beginning.
  713. L20:    MOV     [SI].END_TDATA,BX    ; Save new END_TDATA.
  714.  
  715.                 ; (Implicit interlock with interrupt level):
  716.         INC     [SI].SIZE_TDATA      ; One more character in x-mit buffer.
  717.  
  718.     MOV    DX,[SI].COMX_BASE
  719.         ADD    DX,IER          ; See if tx interrupts are enabled.
  720.         IN      AL,DX
  721.         AND     AL,TXINT
  722.         OR      AL,AL
  723.          JNZ     L22
  724.         MOV     AL,RXINT+TXINT  ; If not then set them.
  725.         OUT     DX,AL
  726. L22:                ; (End of implicit interlock)
  727.  
  728.     MOV    AX,TRUE        ; Indicate all's OK.
  729.     JMP SHORT L26        ; Go join common return code.
  730.  
  731. L24:    MOV    AX,FALSE    ; No more space in buffer.
  732.  
  733. L26:    BEND    COM_PUTC
  734.  
  735.     .SBHED    <COM_LOOPC -- Write to the Input Buffer>
  736.  
  737. ; bool            /* Returns FALSE if no more room. */
  738. ; com_loopc(unit, ch)    /* Writes a character to the input buffer. */
  739. ; int unit;        /* 1 ==> COM1:, 2 ==> COM2:. */
  740. ; char ch;        /* The character to write. */
  741.  
  742.     BENTRY    COM_LOOPC <UNIT,OCHAR>
  743.  
  744.     MOV    AX,UNIT        ; Point to COM1: or COM2: save area.
  745.     CALL    SET_SI        ;
  746.  
  747.         CLI            ; ******* Disable Interrupts *******
  748.     MOV    DX,[SI].RBUF_SIZE ;;; Get the size of the receive buffer.
  749.         CMP     [SI].SIZE_RDATA,DX    ;;; See if any room for more data.
  750.          JAE    L32             ;;; If no room then quit.
  751.  
  752.     MOV    ES,[SI].RBUF_SEG    ;;; Get receive buffer segment number.
  753.     MOV    DI,[SI].RBUF_OFF    ;;; Get receive buffer offset.
  754.         MOV     BX,[SI].END_RDATA    ;;; BX points to free space.
  755.         MOV     AL,OCHAR    ;;; Get data.
  756.         MOV     ES:[BX+DI],AL    ;;; Send data to buffer.
  757.         INC     [SI].SIZE_RDATA      ;;; Got one more character.
  758.         INC     BX              ;;; Increment END_RDATA pointer.
  759.         CMP     BX,DX        ;;; See if gone past end.
  760.          JL    L30             ;;; If not then skip,
  761.         XOR     BX,BX        ;;;   else adjust to beginning.
  762. L30:    MOV     [SI].END_RDATA,BX    ;;; Save value.
  763.  
  764.         STI            ;;; ******* Enable Interrupts *******
  765.     MOV    AX,TRUE        ;;; Indicate success.
  766.     JMP SHORT L34        ; Go join common return code.
  767.  
  768. L32:    STI            ;;; ******* Enable Interrupts *******
  769.     MOV    AX,FALSE    ;;; Indicate no more room.
  770.  
  771. L34:    BEND    COM_LOOPC
  772.  
  773.     .SBHED    <COM_BON -- Turn On BREAK Condition>
  774.  
  775. ; void
  776. ; com_bon(unit)        /* Turns on BREAK. */
  777. ; int unit;        /* 1 ==> COM1:, 2 ==> COM2:. */
  778. ;
  779. ;    Causes the UART to send the BREAK condition.
  780.  
  781.     BENTRY    COM_BON <UNIT>
  782.  
  783.     MOV    AX,UNIT        ; Point to COM1: or COM2: save area.
  784.     CALL    SET_SI        ;
  785.  
  786.     MOV    DX,[SI].COMX_BASE
  787.     ADD    DX,LCR
  788.         MOV     AL,BREAK        ; Set break condition.
  789.         OUT     DX,AL
  790.  
  791.     BEND    COM_BON
  792.  
  793.     .SBHED    <COM_BOFF -- Turn Off BREAK Condition>
  794.  
  795. ; void
  796. ; com_boff(unit)    /* Turns off BREAK. */
  797. ; int unit;        /* 1 ==> COM1:, 2 ==> COM2:. */
  798. ;
  799. ;    Returns the transmit line to the normal state.
  800.  
  801.     BENTRY    COM_BOFF <UNIT>
  802.  
  803.     MOV    AX,UNIT        ; Point to COM1: or COM2: save area.
  804.     CALL    SET_SI        ;
  805.  
  806.     MOV    DX,[SI].COMX_BASE
  807.     ADD    DX,LCR
  808.         MOV     AL,MODE        ; Restore the line control register.
  809.         OUT     DX,AL
  810.  
  811.     BEND    COM_BOFF
  812.  
  813.     .SBHED    <COM_BREAK -- Complete BREAK Sequence>
  814.  
  815. ; void
  816. ; com_break(unit)    /* Sends complete BREAK sequence. */
  817. ; int unit;        /* 1 ==> COM1:, 2 ==> COM2:. */
  818.  
  819.     BENTRY    COM_BREA <UNIT>
  820.  
  821.     MOV    AX,UNIT        ; Point to COM1: or COM2: save area.
  822.     CALL    SET_SI        ;
  823.  
  824.     MOV    DX,[SI].COMX_BASE
  825.     ADD    DX,LCR
  826.         MOV     AL,BREAK        ; Set break condition.
  827.         OUT     DX,AL
  828.  
  829.     MOV    CX,0        ; Wait a while.
  830. WAIT:    LOOP    WAIT
  831.  
  832.         MOV     AL,MODE        ; Restore the line control register.
  833.         OUT     DX,AL
  834.  
  835.     BEND    COM_BREA
  836.  
  837.     ENDPS
  838.         END
  839.